package com.study.Interceptor;

import cn.hutool.core.util.ObjectUtil;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.study.common.UserContext;
import com.study.common.UserInfoDTO;
import com.study.constant.RedisKeyConstant;
import com.study.convention.errorCode.BaseErrorCode;
import com.study.convention.exception.ClientException;
import com.study.utils.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

/**
 * @author canace
 * @version 1.0
 * @description jwt拦截器，验证token
 * @date 2024/1/23 18:21
 */

@Slf4j
@RequiredArgsConstructor
public class JwtInterceptor implements HandlerInterceptor {

    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 获取请求头中的token
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            log.debug("token is null");
            throw new ClientException(BaseErrorCode.TOKEN_IS_NULL);
        }

        try {
            // 校验token
            DecodedJWT decodedJwt = JwtUtils.verifyToken(token);

            // token有效，则把用户信息放入本地线程中
            UserInfoDTO userInfoDTO = UserInfoDTO.builder()
                    .userId(decodedJwt.getClaim("userId").asString())
                    .username(decodedJwt.getClaim("username").asString())
                    .realName(decodedJwt.getClaim("realName").asString())
                    .build();

            // 再从redis中获取token，如果token不存在，说明token已经被注销
            Object userInfo = stringRedisTemplate.opsForHash().get(RedisKeyConstant.USER_LOGIN_KEY + userInfoDTO.getUsername(), userInfoDTO.getUserId());
            if (ObjectUtil.isEmpty(userInfo)) {
                log.debug("token is expire");
                throw new ClientException(BaseErrorCode.TOKEN_IS_EXPIRE);
            }

            // 把用户信息放入本地线程中
            UserContext.setUser(userInfoDTO);
        } catch (Exception e) {
            // token无效就返回异常
            log.debug("token is expire");
            throw new ClientException(BaseErrorCode.TOKEN_IS_EXPIRE);
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {

        // 当前用户操作结束，直接将线程池中的用户信息清除
        UserContext.removeUser();
    }

}
